// Copyright (C) Microsoft Corporation. All rights reserved.

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Serialization;

namespace Microsoft.VSPowerToys.VSCmdShell
{
    /// <summary>
    /// A generic circular linked list class implementation that extends regular linked list
    /// </summary>
    /// <typeparam name="T"></typeparam>
    [Serializable]
    public class WrappingCollection<T> : LinkedList<T>
    {
        /// <summary>
        /// Current node that our index points to.
        /// </summary>
        private LinkedListNode<T> currentNode;

        #region Public Properties

        /// <summary>
        /// Gets the node that list currently points to
        /// </summary>
        public LinkedListNode<T> CurrentNode
        {
            get
            {
                if (this.currentNode == null && this.Count > 0)
                {
                    this.currentNode = this.First;
                }
                return this.currentNode;
            }
            protected set
            {
                this.currentNode = value;
            }
        }

        /// <summary>
        /// Gets the next element on the list.
        /// </summary>
        /// <returns></returns>
        /// <exception cref="InvalidOperationException">Throws IndexOutOfRangeException if list is empty</exception>
        public T Next
        {
            get
            {
                if (this.Count == 0)
                {
                    throw new InvalidOperationException();
                }
                T returnValue = this.CurrentNode.Value;
                if (this.Count > 1 && this.CurrentNode != this.Last)
                {
                    currentNode = this.currentNode.Next;
                }
                else
                {
                    currentNode = this.First;
                }
                return returnValue;
            }
        }

        /// <summary>
        /// Gets the previous element on the list.
        /// </summary>
        /// <returns></returns>
        /// <exception cref="InvalidOperationException">Throws IndexOutOfRangeException if list is empty</exception>
        public T Previous
        {
            get
            {
                if (this.Count == 0)
                {
                    throw new InvalidOperationException();
                }
                if (this.Count > 1 && this.CurrentNode != this.First)
                {
                    currentNode = this.currentNode.Previous;
                }
                else
                {
                    currentNode = this.Last;
                }
                return this.CurrentNode.Value;
            }
        }

        #endregion

        #region Constructors
        /// <summary>
        /// Default constructor
        /// </summary>
        public WrappingCollection() : base()
        {
        }

        /// <summary>
        /// Constructor required for implementing ISerializable
        /// </summary>
        /// <param name="info"></param>
        /// <param name="context"></param>
        protected WrappingCollection(SerializationInfo info, StreamingContext context)
            : base(info, context)
        {
        }

        #endregion

        /// <summary>
        /// Resets the current index to the beginning of the list.
        /// </summary>
        public void ResetPosition()
        {
            this.currentNode = null;
        }

        /// <summary>
        /// Required to implement ISerializable correctly
        /// </summary>
        /// <param name="info"></param>
        /// <param name="context"></param>
        [System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.Demand, SerializationFormatter=true)]
        public override void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            base.GetObjectData(info, context);
        }

        #region Methods - Overrides

        /// <summary>
        /// Clears the contents of the list and also resets the current node pointer
        /// </summary>
        public new void Clear()
        {
            base.Clear();
            this.ResetPosition();
        }

        /// <summary>
        /// Removes a node from the linked list and also resets the current node pointer
        /// </summary>
        /// <param name="toRemove"></param>
        public new void Remove(LinkedListNode<T> toRemove)
        {
            base.Remove(toRemove);
            this.ResetPosition();
        }

        /// <summary>
        /// Removes the given value from the linked list and also resets the current node
        /// pointer if remove is successful
        /// </summary>
        /// <param name="value"></param>
        public new bool Remove(T value)
        {
            bool result = base.Remove(value);
            if (result) this.ResetPosition();
            return result;
        }

        #endregion
    }
}
